#ifndef STRVEC_H
#define STRVEC_H

#include <string>
#include <memory>
#include <utility>
/**
 * @brief 类 vector 类内存分配策略的简化实现
 * 
 */
class StrVec
{
private:
    static std::allocator<std::string> alloc; // 分配元素
    void chk_n_alloc(); // 被添加元素的函数所使用
    std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*); // 工具函数，被拷贝构造函数、赋值运算符和析构函数所使用
    void free(); // 销毁元素并释放内存
    void reallocate(); // 获得更多内存并拷贝已有元素
    std::string *elements; // 指向数组首元素的指针
    std::string *first_free; // 指向数组第一个空闲元素的指针
    std::string *cap; // 指向数组尾后位置的指针
public:
    StrVec(); // allocator 成员进行默认初始化
    StrVec(const StrVec &); // 拷贝构造函数
    StrVec& operator = (const StrVec&); // 拷贝赋值运算符
    StrVec(StrVec &&) noexcept; // 移动构造函数
    StrVec& operator= (StrVec &&) noexcept; // 移动赋值运算符
    ~StrVec(); // 析构函数
    void push_back(const std::string&); // 拷贝元素
    void push_back(std::string &&); // 移动元素
    size_t size() const ;
    size_t capacity() const;
    std::string *begin() const;
    std::string *end() const;
};
/**
 * @brief Construct a new Str Vec:: Str Vec object
 *        allocator 成员进行默认初始化
 * 
 */
StrVec::StrVec() :
    elements(nullptr), first_free(nullptr), cap(nullptr)
{
}
/**
 * @brief Construct a new Str Vec:: Str Vec object
 *        拷贝构造函数
 * 
 * @param s 
 */
StrVec::StrVec(const StrVec &s)
{
    // 调用 alloc_n_copy 分配空间以容纳与 s 中一样多的元素
    auto newdata = alloc_n_copy(s.begin(), s.end());
    elements = newdata.first;
    first_free = newdata.second;
}
/**
 * @brief Construct a new Str Vec:: Str Vec object
 *        移动构造函数
 * 
 * @param s 
 */
StrVec::StrVec(StrVec &&s) noexcept
    : elements(s.elements), first_free(s.first_free), cap(s.cap) // 成员初始化器接管 s 中的资源
{
    s.elements = s.first_free = s.cap = nullptr; // 令 s 进入这样的状态：对其运行析构函数是安全的
}
/**
 * @brief 移动赋值运算符
 * 
 * @param rhs 
 * @return StrVec& 
 */
StrVec& StrVec::operator= (StrVec &&rhs) noexcept
{
    // 直接检测自赋值
    if(this != &rhs)
    {
        free(); // 释放已有元素
        elements = rhs.elements; // 从 rhs 接管资源
        first_free = rhs.first_free;
        cap = rhs.cap;
        // 将 rhs 置于可析构状态
        rhs.elements = rhs.first_free = rhs.cap = nullptr;
    }
    return *this;
}
/**
 * @brief Destroy the Str Vec:: Str Vec object
 * 
 */
StrVec::~StrVec()
{
    free();
}
/**
 * @brief 拷贝赋值运算符
 * 
 * @param rhs 
 * @return StrVec& 
 */
StrVec &StrVec::operator=(const StrVec &rhs)
{
    // 调用 alloc_n_copy 分配内存，大小与 rhs 中元素占用空间一样多
    auto data = alloc_n_copy(rhs.begin(), rhs.end());
    free();
    elements = rhs.elements;
    first_free = cap = data.second;
    return *this;
}
/**
 * @brief 返回当前真正使用的元素的数目
 * 
 * @return size_t 
 */
size_t StrVec::size() const
{
    return first_free - elements;
}
/**
 * @brief 返回 StrVec 可以保存的元素的数量
 * 
 * @return size_t 
 */
size_t StrVec::capacity() const
{
    return cap - elements;
}
/**
 * @brief 返回首元素
 * 
 * @return std::string* 
 */
std::string* StrVec::begin() const
{
    return elements;
}
/**
 * @brief 返回尾元素后一个位置
 * 
 * @return std::string* 
 */
std::string* StrVec::end() const
{
    return first_free;
}
/**
 * @brief 当没有空间容纳新元素，重新分配内存
 * 
 */
void StrVec::chk_n_alloc()
{
    if(size() == capacity())
    {
        reallocate();
    }
}
/**
 * @brief 在尾部添加新元素
 * 
 * @param s 
 */
void StrVec::push_back(const std::string& s)
{
    chk_n_alloc(); // 确保有空间容纳新元素
    alloc.construct(first_free++, s); // 在 first_free 指向的元素中构造 s 的副本
}
/**
 * @brief 移动元素
 * 
 * @param s 
 */
void StrVec::push_back(std::string &&s)
{
    chk_n_alloc();
    alloc.construct(first_free++, std::move(s));
}
/**
 * @brief 分配足够的内存来保存给定范围的元素，并将这些元素拷贝到新分配的内存中
 * 
 * @param b 指向新空间的开始位置
 * @param e 指向拷贝的尾后的位置
 * @return std::pair<std::string*, std::string*> 
 */
std::pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{
    // 分配空间保存给定范围中的元素
    auto data = alloc.allocate(e - b);
    // 初始化并返回一个 pair，改 pair 由 data 和 uninitialized_copy 的返回值构成
    return { data, std::uninitialized_copy(b, e, data) }; // 列表初始化返回值
}
/**
 * @brief 销毁元素，释放内存空间
 * 
 */
void StrVec::free()
{
    // 不能传递给 deallocate 一个空指针，如果 elements 为 0，函数什么也不做
    if(elements)
    {
        // 逆序销毁元素
        for(auto p = first_free; p != elements; )
        {
            alloc.destroy(--p);
        }
        alloc.deallocate(elements, cap - elements);
    }
}
/**
 * @brief 重新分配内存空间
 * 
 */
void StrVec::reallocate()
{
    // 我们将分配当前大小两倍的内存空间
    auto newcapacity = size() ? 2 * size() : 1;
    // 分配新内存
    auto newdata = alloc.allocate(newcapacity);
    // 将数据从旧内存移动到新内存
    auto dest = newdata; // 指向新数组中下一个空闲位置
    auto elem = elements; // 指向旧数组中下一个元素
    for(size_t i = 0; i != size(); ++i)
    {
        alloc.construct(dest++, std::move(*elem++));
    }
    free(); // 一旦我们移动完元素就释放旧内存空间
    // 更新我们的数据结构，执行新元素
    elements = newdata;
    first_free = dest;
    cap = elements + newcapacity;
}


#endif